package game;

import com.sun.j3d.utils.image.TextureLoader;

import javax.media.j3d.*;
import javax.vecmath.Color4f;
import javax.vecmath.Point2d;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;

public class CueStick implements MouseListener, MouseMotionListener {

    public static enum State {
        MOVE,
        POWER,
        HIDDEN
    };

    public State state;

    public float currentAngle;
    public float currentPower;

    public BranchGroup BG;
    public TransformGroup TG;
    public Transform3D T3D;
    public Node shape;

    private MouseEvent lastMouseEvent;

    public CueStick() {
        BG = new BranchGroup();
        BG.setCapability(BranchGroup.ALLOW_DETACH);
        TG = new TransformGroup();
        TG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        TG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        BG.addChild(TG);
        T3D = new Transform3D();
        TG.setTransform(T3D);
        shape = createShape();
        TG.addChild(shape);
    }

    /**
     * Set the current state of the stick
     * @param state
     */
    public void setState(State state) {
        switch (state) {
            case MOVE:
                addToScene();
                addMouseListener();
                break;
            case POWER:
                break;
            case HIDDEN:
                removeFromScene();
                removeMouseListener();
                break;
        }
        this.state = state;
    }

    /**
     * Adds the mouse listener.
     */
    public void addMouseListener() {
        App.addMouseListener(this, this);
    }

    /**
     * Removes the mouse listener.
     */
    public void removeMouseListener() {
        App.removeMouseListener(this, this);
    }

    /**
     * Remove the stick from the scene
     */
    public void removeFromScene() {
        BG.detach();
    }

    /**
     * Add the stick to the scene
     */
    public void addToScene() {
        // orient to mouse and reset power
        lastMouseEvent = null;
        currentPower = 0;
        Point position = MouseInfo.getPointerInfo().getLocation();
        Point offset = App.canvas3D.getLocationOnScreen();
        orientStick(new Point2d(position.x - offset.x, position.y - offset.y));

        App.addToScene(BG);
    }

    /**
     * Handle mouse move.
     * Changes angle or power depending on state
     * @param mouseEvent
     */
    public void mouseMoved(MouseEvent mouseEvent) {
        orientStick(new Point2d(mouseEvent.getX(), mouseEvent.getY()));
    }

    /**
     * Orient the stick based on a target point
     * @param target Target point
     */
    public void orientStick(Point2d target) {
        Point2d pixelPoint = App.cueBall.getPixelLocation();
        float degrees = (float)Math.atan2(target.x - pixelPoint.x, -pixelPoint.y + target.y);
        position(degrees, .05f);
        currentAngle = degrees;
    }

    /**
     * Mouse dragged gets called when holding down
     * @param mouseEvent
     */
    public void mouseDragged(MouseEvent mouseEvent) {
        float deltaY = 0;
        if (lastMouseEvent != null) {
            deltaY = lastMouseEvent.getY() - mouseEvent.getY();
        }
        lastMouseEvent = mouseEvent;

        currentPower = Math.max(0, Math.min(currentPower + deltaY, 100));
        position(currentAngle, .05f + currentPower / (100f / .2f));
    }

    /**
     * Position stick at the cueball.
     * @param angle Angle of rotation around ball in degrees.
     * @param distance Distance from ball
     */
    private void position(float angle, float distance) {
        Vector2f position = App.cueBall.position;
        Transform3D t3d = new Transform3D();
        Transform3D trans = new Transform3D();
        trans.setTranslation(new Vector3f(0, distance, 0));
        t3d.rotZ(angle);
        t3d.mul(trans);
        trans.setTranslation(new Vector3f(position.x, position.y, 0));
        trans.mul(t3d);
        TG.setTransform(trans);
    }

    /**
     * Handle mouse down.
     * Sets state to power.
     * @param mouseEvent Mouse event
     */
    public void mousePressed(MouseEvent mouseEvent) {
        this.setState(State.POWER);
    }

    /**
     * Handle mouse up.
     * Shoots the ball.
     * @param mouseEvent Mouse event
     */
    public void mouseReleased(MouseEvent mouseEvent) {
        App.shoot(currentAngle, currentPower);
    }

    public void mouseEntered(MouseEvent mouseEvent) { }
    public void mouseExited(MouseEvent mouseEvent) { }
    public void mouseClicked(MouseEvent mouseEvent) { }

    /**
     * Create the shape for
     * @return shape
     */
    public Node createShape() {
        Node node = null;
        try {
            TextureLoader myLoader = new TextureLoader( new File("src/cuestick.png").toURI().toURL(), TextureLoader.GENERATE_MIPMAP, null);

            Appearance app = new Appearance();
            app.setTexture(myLoader.getTexture());

            TransparencyAttributes ta =  new TransparencyAttributes();
            ta.setTransparencyMode(TransparencyAttributes.NICEST);
            app.setTransparencyAttributes( ta );

            QuadArray polygon1 = new QuadArray (4, QuadArray.COORDINATES | QuadArray.TEXTURE_COORDINATE_2);
            polygon1.setCoordinate (0, new Point3f(-.0125f, 0f, 0f));
            polygon1.setCoordinate (1, new Point3f (.0125f, 0f, 0f));
            polygon1.setCoordinate (2, new Point3f (.0125f, .75f, .2f));
            polygon1.setCoordinate (3, new Point3f (-.0125f, .75f, .2f));

            TexCoord2f[] textCoord = {	new TexCoord2f(1.0f,1.0f),
                    new TexCoord2f(1.0f,0.0f),
                    new TexCoord2f(0.0f,0.0f),
                    new TexCoord2f(0.0f,1.0f)};

            polygon1.setTextureCoordinates(0, 0, textCoord);

            node = new Shape3D(polygon1, app);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return node;
    }
}
